/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package futbol.tacticas.concursantes.Potrosa;

import java.awt.Point;

/**
 *
 * @author IGadea
 */
public class Jugador {

    public enum TIPO {GUARDAMETA, DEFENSA, MITAD, DELANTERO};
    public enum TAREA { MARCAR, DESMARCARSE, DESPLAZARSE, SUBIR_BALON, 
                        INTERCEPTAR_BALON, CHUTAR, PASAR, ESPERAR };
    public enum SITUACION { ATAQUE, DEFENSA };

    private Observador observador = Observador.instancia();

    private int numero = -1;
    private TIPO rol = TIPO.MITAD;
    private int fuerza;
    private int velocidad;
    private Entrenador entrenador;

    // Comandos basicos
    private Point posicionDefensa = new Point(0,0);
    private Point posicionAtaque = new Point(0,0);
    private Point posicionActual = new Point(0,0);
    private Point posicionDestino = new Point(0,0);
    private Point golpearBalon = new Point(0,0);
    private boolean ordenMovimiento;
    private boolean chutandoBalon;
    private boolean tiroAPuerta;
    private boolean noCambiarDireccion = false;
    private boolean marcando = false;
    private int marcadores[] = new int[11];

    public Jugador(int numero, int fuerza, Entrenador entrenador) {
        this.numero = numero;
        this.fuerza = Utilidades.control(fuerza, 4, 7);
        this.velocidad = 10 - this.fuerza;
        this.noCambiarDireccion = false;
        this.entrenador = entrenador;
    }

    public TIPO rol() {
        return rol;
    }

    public void cambiarRol(TIPO rol) {
        this.rol = rol;
    }
    
    public void alineacion(Point defensa, Point ataque) {
        posicionDefensa = defensa;
        posicionAtaque = ataque;
    }
    
    public Point posicionActual() {
        return posicionActual;
    }

    public boolean posicionDestino(Point donde) {
        donde.setLocation(posicionDestino);
        return ordenMovimiento;
    }
    
    public void irA(Point destino) {
        ordenMovimiento = true;
        posicionDestino = destino;
    }

    public void tirarAPuerta() {
        tiroAPuerta = true;
    }
    
    public boolean tirandoAPuerta() {
        return tiroAPuerta;
    }
    
    public void golpearBalon(Point destino) {
        chutandoBalon = true;
        golpearBalon = destino;
    }

    public boolean golpeandoBalon(Point donde) {
        donde.setLocation(golpearBalon);
        return chutandoBalon;
    }
    
    public int fuerza() {
        return fuerza;
    }
    
    public int velocidad() {
        return velocidad;
    }

    public int numero() {
        return numero;
    }

    public void guardarPosicion(Point posicion) {
        posicionActual = posicion;
        chutandoBalon = false;
        tiroAPuerta = false;
        marcando = false;
        ordenMovimiento = false;
    }

    public boolean estaMarcado(boolean marcadorRival[]) {
        boolean estaMarcado = false;
        if (rol != TIPO.GUARDAMETA) {
            for (int n=0; n<11; n++) {
                if (!marcadorRival[n]) {
                    if (observador.distanciaRival(this, n) < 25) {
                        marcadores[n] = Math.min(marcadores[n] + 2, 13);
                        if (marcadores[n] > 10) {
                            marcadorRival[n] = true;
                            estaMarcado = true;
                            break;
                        }
                    }
                }
            }
        }
        for (int n=0; n<11; n++)
            marcadores[n] = Math.max(marcadores[n] - 1, 0);

        return estaMarcado;
    }
    
    public double posibilidadMarcaje(int rival) {
        double TASA_DISTANCIA = 0.45;
        double TASA_VELOCIDAD = 0.20;
        double TASA_POSICION  = 0.15;
        double TASA_ROL       = 0.20;
        double prob = 0.0;

        if (rival == -1)
            return 0.0;
        
        // Los porteros no pueden marcar!!
        if (rol == TIPO.GUARDAMETA)
            prob = 0.0;
        else {
            double distancia = observador.distanciaRival(posicionActual, rival);
            prob += TASA_DISTANCIA - (distancia * TASA_DISTANCIA / 400);

            int difVelocidad = 3 + velocidad() - observador.velocidadRival(rival);
            prob += difVelocidad * TASA_VELOCIDAD / 6;

            double posicion = observador.distanciaRival(posicionDefensa, rival);
            prob += TASA_POSICION - (posicion * TASA_POSICION / 400);

            if (rol == TIPO.DEFENSA)
                prob += TASA_ROL;
            else if (rol == TIPO.MITAD)
                prob += TASA_ROL/2;
            else if (rol == TIPO.DELANTERO)
                prob += TASA_ROL/4;
        }

        return prob;
    }

    public Observador.MejorDestino interceptarBalon() {
        Observador.MejorDestino md = observador.interceptarTrayectoria2(numero);
        out((numero+1) + " en (" + posicionActual.x + "," + posicionActual.y + 
                ") siguiendo la trayectoria (" + md.interseccion.x + "," + md.interseccion.y + 
                ") con p(x)=" + md.probabilidad + " en " + md.turnos + " turnos");
        return md;
    }
    
    public double asignarTarea(TAREA tarea, SITUACION situacion, int jugador, Point lugar) {
        double resultado = 0.0;

        if (jugador == -1)
            return 0;
        
        switch (tarea) {
            case MARCAR:
                int desX = 0;
                int desY = 0;
                if (lugar != null) {
                    desX = lugar.x;
                    desY = lugar.y;
                }

                MathVector mv = observador.vectorRival(jugador);
                int x = (int)(mv.origenX() + mv.vectorX() *  (mv.tamanyo() - 10 + desX));
                int y = (int)(mv.origenY() + mv.vectorY() *  (mv.tamanyo() - 10 + desY));
                irA(new Point(x,y));
                marcando = true;
                resultado = 1.0;
                break;

            case DESMARCARSE:
                if ((rol != TIPO.GUARDAMETA) && !marcando) {
                    Point base = posicionBase(situacion);
                    Point posicion = buscarMejorPosicion(posicionActual, base);
                    irA(posicion);
                    resultado = 1.0;
                }
                break;

            case DESPLAZARSE:
                irA(lugar);
                resultado = 1.0;
                break;
                
            case SUBIR_BALON:
                // Descartado: Ahora se hace automaticamente priorizando pases cortos.
                break;

            case INTERCEPTAR_BALON:
                // Descartado: Ahora se hace manualmente y controlado por el Entrenador
                break;

            case CHUTAR:
                double prob = observador.probabilidadDeMarcar(observador.posicionBalon(), fuerza, true);
                out("Probabilidad de marcar = " + prob + " por el jugador " + (numero+1));
                if (prob >= 0.5) {
                    tirarAPuerta();
                    observador.avisarPase(fuerza() * 3);
                    resultado = prob;
                }
                break;

            case PASAR:
                if (observador.distanciaJugador(numero) <= 11) {
                    Observador.MejorDestino mejorPase = observador.buscarMejorPase(numero, jugador==1);
                    if (mejorPase.probabilidad > 0.60) {
                        if (mejorPase.interseccion != null)
                            out("Interseccion=(" + mejorPase.interseccion.x + "," + mejorPase.interseccion.y + ") en " + mejorPase.turnos + " turnos");
                        Point donde = corregirBugPosicion(mejorPase.lugar, mejorPase.distancia);

                        if ((mejorPase.jugadores != null) && (observador.velocidadBalon() <= 15) &&
                                                             (mejorPase.interseccion != null)) {
                            int j = mejorPase.jugadores[0];
                            if (j >= 0) {
                                observador.miJugador(j).asignarTarea(TAREA.DESPLAZARSE, SITUACION.ATAQUE, 
                                                                     0, mejorPase.interseccion);
                                out("Avisar a " + (j+1));
                            }
                        }
                        golpearBalon(donde);
                        observador.avisarPase(mejorPase.velocidad);
                        resultado = mejorPase.probabilidad;
                    } else {
                        out("No hay probabilidad suficiente para pasar");
                    }
                } else {
                    out("Jugador mas cercano: " + (numero+1) + " Distancia=" + observador.distanciaJugador(numero));
                }
                break;

            case ESPERAR:
                irA(posicionBase(situacion));
                resultado = 1.0;
                break;
        }

        return resultado;
    }
    
    public Point posicionBase(SITUACION situacion) {
        if (situacion == SITUACION.ATAQUE)
            return posicionAtaque;
        else
            return posicionDefensa;
    }
    
    public boolean paseSeguro() {
        boolean res = true;

        // Cada jugador contrario es un obstaculo.
        for (int i = 0;  i < 11;  i++) {
            double d = Utilidades.distanciaPuntoSegmento(
                            observador.posicionBalon().x, observador.posicionBalon().y,
                            posicionActual.x, posicionActual.y, 
                            observador.posicionRival(i).x, observador.posicionRival(i).y);
            if (d < 30.0) {
                res = false;
                break;
            }
        }
        return res;
    }

    private Point corregirBugPosicion(Point donde, int distancia) {
        MathVector mv = new MathVector(observador.posicionBalon().x, observador.posicionBalon().y,
                                       donde.x, donde.y);
        int posicion = (int)mv.tamanyo() + 10;

        Point lugar;
        double tanteo;
        do {
            lugar = observador.puntoDistante(mv, posicion, false);
            MathVector aux = new MathVector(posicionActual.x, posicionActual.y, lugar.x, lugar.y);
            tanteo = aux.tamanyo();
            posicion--;
        } while (tanteo > distancia);

        return lugar;
    }

    private Point buscarMejorPosicion(Point posicion, Point base) {
        double FUERZA_JUGADOR     = -30.0;
        double FUERZA_RIVAL       = -60.0;
        double FUERZA_META_RIVAL  = +20.0;
        double FUERZA_META_PROPIA = -35.0;
        double FUERZA_ESQUINA     = -50.0;
        double FUERZA_BASE        = +60.0;
        double FUERZA_PARED       = -25.0;
        double RADIO_ACCION       =  80.0;

        Point direc;

        // Fuerza atractiva de la porteria rival.
        double distancia = (140 - posicion.x) * (140 - posicion.x) + (posicion.y * posicion.y);
        double sumaVectorialX = FUERZA_META_RIVAL * (140 - posicion.x) / distancia;
        double sumaVectorialY = FUERZA_META_RIVAL * (0   - posicion.y) / distancia;

        distancia = (70 - posicion.x) * (70 - posicion.x) + (posicion.y * posicion.y);
        sumaVectorialX += FUERZA_META_RIVAL * (70 - posicion.x) / distancia;
        sumaVectorialY += FUERZA_META_RIVAL * ( 0 - posicion.y) / distancia;

        // Sumar la fuerza base (posicion tactica).
        distancia = (base.x - posicion.x) * (base.x - posicion.x) +
                    (base.y - posicion.y) * (base.y - posicion.y);
        sumaVectorialX += FUERZA_BASE * (base.x - posicion.x) / distancia;
        sumaVectorialY += FUERZA_BASE * (base.y - posicion.y) / distancia;
        
        // Fuerza repulsiva de la porteria propia.
        distancia = (-160 - posicion.x) * (-160 - posicion.x) + (posicion.y * posicion.y);
        sumaVectorialX += FUERZA_META_PROPIA * (-160 - posicion.x) / distancia;
        sumaVectorialY += FUERZA_META_PROPIA * ( 0   - posicion.y) / distancia;

        // Campo de fuerza de las paredes que mantiene a los jugadores dentro del campo.
        sumaVectorialY += FUERZA_PARED / (-120 - posicion.y);
        sumaVectorialY += FUERZA_PARED / ( 120 - posicion.y);
        sumaVectorialX += FUERZA_PARED / (-160 - posicion.x);
        sumaVectorialX += FUERZA_PARED / ( 160 - posicion.x);

        // Fuerza repulsiva de las esquinas.
        Point esquina[] = { new Point(-160,-120), new Point(-160,120), 
                            new Point(160,-120),  new Point(160,120) };
        for (int i=0; i<4; i++) {
            distancia = (esquina[i].x - posicion.x) * (esquina[i].x - posicion.x) +
                        (esquina[i].y - posicion.y) * (esquina[i].y - posicion.y);
            sumaVectorialX += FUERZA_ESQUINA * (esquina[i].x - posicion.x) / distancia;
            sumaVectorialY += FUERZA_ESQUINA * (esquina[i].y - posicion.y) / distancia;
        }
        
        // Fuerza repulsiva de los rivales.
        for (int i=0; i<11; i++) {
            distancia = (observador.posicionRival(i).x - posicion.x) * (observador.posicionRival(i).x - posicion.x) + 
                        (observador.posicionRival(i).y - posicion.y) * (observador.posicionRival(i).y - posicion.y);

            if (distancia < RADIO_ACCION*RADIO_ACCION) {
                sumaVectorialX += FUERZA_RIVAL * (observador.posicionRival(i).x - posicion.x) / distancia;
                sumaVectorialY += FUERZA_RIVAL * (observador.posicionRival(i).y - posicion.y) / distancia;
            }
        }

        for (int i=0; i<11; i++) {
            if (i != numero) {
                Point miJugador = observador.miJugador(i).posicionActual();
                distancia = (miJugador.x - posicion.x) * (miJugador.x - posicion.x) + 
                            (miJugador.y - posicion.y) * (miJugador.y - posicion.y);

                if (distancia < RADIO_ACCION*RADIO_ACCION) {
                    sumaVectorialX += FUERZA_JUGADOR * (miJugador.x - posicion.x) / distancia;
                    sumaVectorialY += FUERZA_JUGADOR * (miJugador.y - posicion.y) / distancia;
                }
            }
        }
        
        // Calculo de la suma vectorial final y direccion de avance.
        MathVector mv = new MathVector(0, 0, (int)(10000*sumaVectorialX), (int)(10000*sumaVectorialY));
        direc = new Point(posicion.x + (int)(10*mv.vectorX()),
                          posicion.y + (int)(10*mv.vectorY()));

        // Corregir la trayectoria si un jugador tiende a salirse.
        if (Math.abs(direc.x) > 160) direc.x = 0;
        if (Math.abs(direc.y) > 120) direc.y = 0;
        
        return direc;
    }

    private void out(String str) {
//        System.out.println(str);
    }
}
